home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 8 / The Arsenal Files Collection #8 (Arsenal Computer) (1996).ISO / prg_casm / snip9611.zip / GETOPTS.C < prev    next >
C/C++ Source or Header  |  1996-11-24  |  18KB  |  578 lines

  1. /* +++Date last modified: 24-Nov-1996 */
  2.  
  3. /************************************************************************/
  4. /*                                                                      */
  5. /*  GETOPTS.C - Universal command line options parser                   */
  6. /*                                                                      */
  7. /*  Original Copyright 1990-96 by Robert B. Stout as part of            */
  8. /*  the MicroFirm Function Library (MFL)                                */
  9. /*                                                                      */
  10. /*  The user is granted a free limited license to use this source file  */
  11. /*  to create royalty-free programs, subject to the terms of the        */
  12. /*  license restrictions specified in the LICENSE.MFL file.             */
  13. /*                                                                      */
  14. /************************************************************************/
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <limits.h>
  20. #include "sniptype.h"
  21. #include "filnames.h"
  22. #include "errors.h"
  23. #include "dirport.h"
  24. #include "snipmath.h"
  25. #include "minmax.h"
  26. #include "getopts.h"
  27.  
  28. #define MAX_XARGS 512
  29.  
  30. int         xargc = 0;
  31. char       *xargv[MAX_XARGS]        = {NULL};
  32. Boolean_T   getopts_range_err       = False_;
  33. Boolean_T   xargs_on                = True_;
  34.  
  35. static FILE *rspfile = NULL;
  36. static int   count = 0, argidx = 0;
  37.  
  38. enum proc_stat { PSerror = -1, PSok, PSliteral};
  39.  
  40. static Boolean_T      PASCAL bounds(struct Option_Tag *);
  41. static enum proc_stat PASCAL swProc(char *swStr);
  42. static void           PASCAL argProc(char *argStr);
  43.  
  44. /*
  45. **  getopts()
  46. **
  47. **  Parameters: 1 - argc from main()
  48. **              2 - argv from main()
  49. **              3 - your program's options[] array
  50. **
  51. **  Returns: Number of options specified or Error_
  52. **
  53. **  Notes: 1. Your program should declare the global options[] array which
  54. **            specifies all options recognized by getopts().
  55. **
  56. **         2. Out of range data are coerced into range and getopts_range_err
  57. **            is set True_.
  58. */
  59.  
  60. int getopts(int argc, char *argv[])
  61. {
  62.       int i;
  63.       Boolean_T scanning = True_;
  64.       struct Option_Tag *ptr;
  65.       char rspfname[FILENAME_MAX];
  66.       char newarg[256];
  67.       enum proc_stat ps;
  68.  
  69.       xargc = argc;
  70.       xargv[argidx++] = argv[0];
  71.       for (i = 1, count = 0; i < argc; )
  72.       {
  73.             /*
  74.             ** If necessary, open a response file
  75.             */
  76.  
  77.             if (scanning && !rspfile && '@' == argv[i][0])
  78.             {
  79.                   rspfile = cant(&argv[i][1], "r");
  80.                   --xargc;
  81.                   continue;
  82.             }
  83.  
  84.             /*
  85.             ** Get the next argument
  86.             */
  87.  
  88.             if (rspfile)
  89.             {
  90.                   if (NULL == fgets(newarg, 256, rspfile))
  91.                   {
  92.                         rspfile = NULL;
  93.                         ++i;
  94.                         continue;
  95.                   }
  96.                   else
  97.                   {
  98.                         if ('\n' == LAST_CHAR(newarg))
  99.                               LAST_CHAR(newarg) = NUL;
  100.                   }
  101.             }
  102.             else
  103.             {
  104.                   strcpy(newarg, argv[i++]);
  105.             }
  106.  
  107.             /*
  108.             ** Per Unix tradition, back-to-back switch characters signify
  109.             ** the end of the switches
  110.             */
  111.  
  112.             if ((2 == strlen(newarg)) && strchr("-/", newarg[0]) &&
  113.                   strchr("-/", newarg[1]))
  114.             {
  115.                   scanning = False_;
  116.                   if (!rspfile)
  117.                         --xargc;
  118.                   continue;
  119.             }
  120.  
  121.             if (scanning && (strchr("-/", newarg[0])))
  122.             {
  123.                   ps = swProc(newarg);
  124.  
  125.                   if (PSerror == ps)
  126.                         return Error_;
  127.  
  128.                   if (PSok == ps)
  129.                         continue;
  130.             }
  131.  
  132.             /*
  133.             ** If we got here, newarg must be an argument or filename
  134.             */
  135.  
  136.             argProc(newarg);
  137.       }
  138.       return count;
  139. }
  140.  
  141. /*
  142. **  Static function to process switch statements
  143. **
  144. **  Parameters: 1 - argv[i] containing the switch
  145. **
  146. **  Returns: PSok      if switch successful
  147. **           PSerror   if invalid
  148. **           PSliteral if literal (non-switch) argument
  149. */
  150.  
  151. static enum proc_stat PASCAL swProc(char *swStr)
  152. {
  153.       struct Option_Tag *ptr;
  154.       Boolean_T searching;
  155.       unsigned short byte_var;
  156.       char *arg_ptr;
  157.  
  158.       /*
  159.       ** Found a switch - If the 2nd character is also a switch
  160.       ** character. If so, then it's a literal and is skipped
  161.       */
  162.  
  163.       if (strchr("-/@", swStr[1]))
  164.             return PSliteral;
  165.  
  166.       for (ptr = options, searching = True_; searching; ++ptr)
  167.       {
  168.             if (!ptr->case_sense)
  169.             {
  170.                   ptr->letter = toupper(ptr->letter);
  171.                   swStr[1]    = toupper(swStr[1]);
  172.             }
  173.             if ((int)swStr[1] == ptr->letter) switch (ptr->type)
  174.             {
  175.             case Boolean_Tag:
  176.                   if ('-' == swStr[2])
  177.                         *((Boolean_T *)(ptr->buf)) = False_;
  178.                   else  *((Boolean_T *)(ptr->buf)) = True_;
  179.                   searching = False_;
  180.                   break;
  181.  
  182.             case Byte_Tag:
  183.                   if (!swStr[2])
  184.                   {
  185.                         if (ptr->Default)
  186.                               arg_ptr = ptr->Default;
  187.                         else  return PSerror;
  188.                   }
  189.                   else  arg_ptr = &swStr[2];
  190.  
  191.                   sscanf(arg_ptr, "%hx", &byte_var);
  192.                   *((char *)(ptr->buf)) = (unsigned char)(byte_var & 0xff);
  193.                   bounds(ptr);
  194.                   searching = False_;
  195.                   break;
  196.  
  197.             case Int_Tag:
  198.                   if (!swStr[2])
  199.                   {
  200.                         if (ptr->Default)
  201.                               arg_ptr = ptr->Default;
  202.                         else  return PSerror;
  203.                   }
  204.                   else  arg_ptr = &swStr[2];
  205.  
  206.                   *((int *)(ptr->buf)) = (int)(dround(getopts_eval(arg_ptr)));
  207.                   bounds(ptr);
  208.                   searching = False_;
  209.                   break;
  210.  
  211.             case Short_Tag:
  212.                   if (!swStr[2])
  213.                   {
  214.                         if (ptr->Default)
  215.                               arg_ptr = ptr->Default;
  216.                         else  return PSerror;
  217.                   }
  218.                   else  arg_ptr = &swStr[2];
  219.  
  220.                   *((short *)(ptr->buf)) =
  221.                         (short)(dround(getopts_eval(arg_ptr)));
  222.                   bounds(ptr);
  223.                   searching = False_;
  224.                   break;
  225.  
  226.             case Word_Tag:
  227.                   if (!swStr[2])
  228.                   {
  229.                         if (ptr->Default)
  230.                               arg_ptr = ptr->Default;
  231.                         else  return PSerror;
  232.                   }
  233.                   else  arg_ptr = &swStr[2];
  234.  
  235.                   sscanf(arg_ptr, "%hx", (unsigned short *)(ptr->buf));
  236.                   bounds(ptr);
  237.                   searching = False_;
  238.                   break;
  239.  
  240.             case Long_Tag:
  241.                   if (!swStr[2])
  242.                   {
  243.                         if (ptr->Default)
  244.                               arg_ptr = ptr->Default;
  245.                         else  return PSerror;
  246.                   }
  247.                   else  arg_ptr = &swStr[2];
  248.  
  249.                   *((long *)(ptr->buf)) =
  250.                         (long)(dround(getopts_eval(arg_ptr)));
  251.                   bounds(ptr);
  252.                   searching = False_;
  253.                   break;
  254.  
  255.             case DWord_Tag:
  256.                   if (!swStr[2])
  257.                   {
  258.                         if (ptr->Default)
  259.                               arg_ptr = ptr->Default;
  260.                         else  return PSerror;
  261.                   }
  262.                   else  arg_ptr = &swStr[2];
  263.  
  264.                   sscanf(arg_ptr, "%lx", (unsigned long *)(ptr->buf));
  265.                   bounds(ptr);
  266.                   searching = False_;
  267.                   break;
  268.  
  269.             case Float_Tag:
  270.                   if (!swStr[2])
  271.                   {
  272.                         if (ptr->Default)
  273.                               arg_ptr = ptr->Default;
  274.                         else  return PSerror;
  275.                   }
  276.                   else  arg_ptr = &swStr[2];
  277.  
  278.                   *((double *)(ptr->buf)) = (double)getopts_eval(arg_ptr);
  279.                   bounds(ptr);
  280.                   searching = False_;
  281.                   break;
  282.  
  283.             case DFloat_Tag:
  284.                   if (!swStr[2])
  285.                   {
  286.                         if (ptr->Default)
  287.                               arg_ptr = ptr->Default;
  288.                         else  return PSerror;
  289.                   }
  290.                   else  arg_ptr = &swStr[2];
  291.  
  292.                   *((double *)(ptr->buf)) = (double)getopts_eval(arg_ptr);
  293.                   bounds(ptr);
  294.                   searching = False_;
  295.                   break;
  296.  
  297.             case String_Tag:
  298.                   if (!swStr[2] && ptr->Default)
  299.                         strcpy(ptr->buf, (char *)(ptr->Default));
  300.                   else  strcpy(ptr->buf, &swStr[2]);
  301.  
  302.                   searching = False_;
  303.                   break;
  304.  
  305.             default:
  306.                   return Error_;
  307.             }
  308.       }
  309.       ++count;
  310.       if (!rspfile)
  311.             --xargc;
  312.  
  313.       return PSok;
  314. }
  315.  
  316. /*
  317. **  Static function to process arguments
  318. */
  319.  
  320. static void PASCAL argProc(char *argStr)
  321. {
  322.       DOSFileData ff;
  323.  
  324.       /* If no wildcards or ignoring wildcards, just copy it */
  325.  
  326.       if (!xargs_on || !has_wild(argStr))
  327.       {
  328.             xargv[argidx] = malloc(strlen(argStr) + 1);
  329.             if (NULL == xargv[argidx])
  330.                   ErrExit("Out of memory");
  331.             strcpy(xargv[argidx], argStr);
  332.             ++argidx;
  333.             return;
  334.       }
  335.       else  /* Expand wildcards, if possible                      */
  336.       {
  337.             if (Success_ == FIND_FIRST(argStr, _A_ANY, &ff))
  338.             {
  339.                   char path[FILENAME_MAX];
  340.                   char *p;
  341.  
  342.                   /* Save the path for re-attachment              */
  343.  
  344.                   fnSplit(argStr, NULL, path, NULL, NULL, NULL, NULL);
  345.  
  346.                   --xargc;    /* We add stuff in the loop, so back up   */
  347.                   do
  348.                   {                             
  349.                         xargv[argidx] = malloc(strlen(ff_name(&ff))
  350.                                                + strlen(path) + 2);
  351.                         if (NULL == xargv[argidx])
  352.                               ErrExit("Out of memory");
  353.                         fnMerge(xargv[argidx], NULL, path, NULL, ff_name(&ff),
  354.                                 NULL, NULL);
  355.                         ++argidx;
  356.                         ++xargc;
  357.  
  358.                   } while (Success_ == FIND_NEXT(&ff));
  359.                   FIND_END(&ff);
  360.             }
  361.       }
  362. }
  363.  
  364. /*
  365. **  Assure new data are within specified ranges, return non-zero if coerced
  366. */
  367.  
  368. static Boolean_T PASCAL bounds(struct Option_Tag *option)
  369. {
  370.       Boolean_T coerced = False_;
  371.       union {
  372.             unsigned char     B;
  373.             int               I;
  374.             short             S;
  375.             unsigned short    W;
  376.             long              L;
  377.             unsigned long     DW;
  378.             float             F;
  379.             double            D;
  380.       } tmp, val;
  381.       
  382.       switch(option->type)
  383.       {
  384.       case Byte_Tag:
  385.             tmp.B = *((unsigned char *)(option->buf));
  386.             if (option->max)
  387.             {
  388.                   sscanf(option->max, "%hx", &val.B);
  389.                   tmp.B = min(tmp.B, val.B);
  390.             }
  391.             if (option->min)
  392.             {
  393.                   sscanf(option->min, "%hx", &val.B);
  394.                   tmp.B = max(tmp.B, val.B);
  395.             }
  396.             if (*((unsigned char *)(option->buf)) != tmp.B)
  397.             {
  398.                   getopts_range_err = True_;
  399.                   *((unsigned char *)(option->buf)) = tmp.B;
  400.                   coerced = True_;
  401.             }
  402.             break;
  403.  
  404.       case Int_Tag:
  405.             tmp.I = *((int *)(option->buf));
  406.             if (option->max)
  407.             {
  408.                   val.D = dround(getopts_eval(option->max));
  409.                   if (val.D > (double)INT_MAX)
  410.                         val.I = INT_MAX;
  411.                   else  val.I = (int)val.D;
  412.                   tmp.I = min(tmp.I, val.I);
  413.             }
  414.             if (option->min)
  415.             {
  416.                   val.D = dround(getopts_eval(option->min));
  417.                   if (val.D < (double)INT_MIN)
  418.                         val.I = INT_MIN;
  419.                   else  val.I = (int)val.D;
  420.                   tmp.I = max(tmp.I, val.I);
  421.             }
  422.             if (*((int *)(option->buf)) != tmp.I)
  423.             {
  424.                   getopts_range_err = True_;
  425.                   *((int *)(option->buf)) = tmp.I;
  426.                   coerced = True_;
  427.             }
  428.             break;
  429.  
  430.       case Short_Tag:
  431.             tmp.S = *((short *)(option->buf));
  432.             if (option->max)
  433.             {
  434.                   val.D = dround(getopts_eval(option->max));
  435.                   if (val.D > (double)SHRT_MAX)
  436.                         val.S = SHRT_MAX;
  437.                   else  val.S = (short)val.D;
  438.                   tmp.S = min(tmp.I, val.I);
  439.             }
  440.             if (option->min)
  441.             {
  442.                   val.D = dround(getopts_eval(option->min));
  443.                   if (val.D < (double)SHRT_MIN)
  444.                         val.S = SHRT_MIN;
  445.                   else  val.S = (short)val.D;
  446.                   tmp.S = max(tmp.I, val.I);
  447.             }
  448.             if (*((short *)(option->buf)) != tmp.S)
  449.             {
  450.                   getopts_range_err = True_;
  451.                   *((short *)(option->buf)) = tmp.I;
  452.                   coerced = True_;
  453.             }
  454.             break;
  455.  
  456.       case Word_Tag:
  457.             tmp.W = *((unsigned short *)(option->buf));
  458.             if (option->max)
  459.             {
  460.                   sscanf(option->max, "%hx", &val.W);
  461.                   tmp.W = min(tmp.W, val.W);
  462.             }
  463.             if (option->min)
  464.             {
  465.                   sscanf(option->min, "%hx", &val.W);
  466.                   tmp.W = max(tmp.W, val.W);
  467.             }
  468.             if (*((unsigned short *)(option->buf)) != tmp.W)
  469.             {
  470.                   getopts_range_err = True_;
  471.                   *((unsigned short *)(option->buf)) = tmp.W;
  472.                   coerced = True_;
  473.             }
  474.             break;
  475.  
  476.       case Long_Tag:
  477.             tmp.L = *((long *)(option->buf));
  478.             if (option->max)
  479.             {
  480.                   val.D = dround(getopts_eval(option->max));
  481.                   if (val.D > (double)LONG_MAX)
  482.                         val.L = LONG_MAX;
  483.                   else  val.L = (long)val.D;
  484.                   tmp.L = min(tmp.L, val.L);
  485.             }
  486.             if (option->min)
  487.             {
  488.                   val.D = dround(getopts_eval(option->min));
  489.                   if (val.D < (double)LONG_MIN)
  490.                         val.L = LONG_MIN;
  491.                   else  val.L = (int)val.D;
  492.                   tmp.L = max(tmp.L, val.L);
  493.             }
  494.             if (*((long *)(option->buf)) != tmp.L)
  495.             {
  496.                   getopts_range_err = True_;
  497.                   *((long *)(option->buf)) = tmp.L;
  498.                   coerced = True_;
  499.             }
  500.             break;
  501.  
  502.       case DWord_Tag:
  503.             tmp.DW = *((unsigned long *)(option->buf));
  504.             if (option->max)
  505.             {
  506.                   sscanf(option->max, "%lx", &val.DW);
  507.                   tmp.DW = min(tmp.DW, val.DW);
  508.             }
  509.             if (option->min)
  510.             {
  511.                   sscanf(option->min, "%hx", &val.DW);
  512.                   tmp.DW = max(tmp.DW, val.DW);
  513.             }
  514.             if (*((unsigned long *)(option->buf)) != tmp.DW)
  515.             {
  516.                   getopts_range_err = True_;
  517.                   *((unsigned long *)(option->buf)) = tmp.DW;
  518.                   coerced = True_;
  519.             }
  520.             break;
  521.  
  522.       case Float_Tag:
  523.             tmp.F = *((float *)(option->buf));
  524.             if (option->max)
  525.             {
  526.                   val.F = (float)getopts_eval(option->max);
  527.                   tmp.F = min(tmp.F, val.F);
  528.             }
  529.             if (option->min)
  530.             {
  531.                   val.F = (float)getopts_eval(option->min);
  532.                   tmp.F = max(tmp.F, val.F);
  533.             }
  534.             if (*((float *)(option->buf)) != tmp.F)
  535.             {
  536.                   getopts_range_err = True_;
  537.                   *((float *)(option->buf)) = tmp.F;
  538.                   coerced = True_;
  539.             }
  540.             break;
  541.  
  542.       case DFloat_Tag:
  543.             tmp.D = *((double *)(option->buf));
  544.             if (option->max)
  545.             {
  546.                   val.D = getopts_eval(option->max);
  547.                   tmp.D = min(tmp.D, val.D);
  548.             }
  549.             if (option->min)
  550.             {
  551.                   val.D = getopts_eval(option->min);
  552.                   tmp.D = max(tmp.D, val.D);
  553.             }
  554.             if (*((double *)(option->buf)) != tmp.D)
  555.             {
  556.                   getopts_range_err = True_;
  557.                   *((double *)(option->buf)) = tmp.D;
  558.                   coerced = True_;
  559.             }
  560.             break;
  561.       }
  562.  
  563.       return coerced;
  564. }
  565.  
  566. /*
  567. **  Simplified evaluate() call - returns double or aborts
  568. */
  569.  
  570. double getopts_eval(char *str)
  571. {
  572.       double retval;
  573.  
  574.       if (Success_ == evaluate(str, &retval))
  575.             return retval;
  576.       else  ErrExit("Error evlauating \"%s\" - aborting\n", str);
  577. }
  578.